package jalgebrava.group.groups;

import static jalgebrava.util.collections.IterableUtil.*;
import jalgebrava.group.Group;
import jalgebrava.util.Pair;
import jalgebrava.util.collections.ImmutableIterable;
import jalgebrava.util.collections.IterableUtil;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public final class DihedralGroupFactory {
	private final static Map<Integer, Group<Pair<Integer, Boolean>>> cache = new HashMap<Integer, Group<Pair<Integer,Boolean>>>();
	final static Group<Pair<Integer, Boolean>> mk(int n) {
		if (n%2==1) {
			throw new RuntimeException("Uneven dihedral group");
		}
		
		// look in cache
		Group<Pair<Integer, Boolean>> result;
		result = cache.get(n);
		if (result != null) {
			return result;
		}
		result = mkWorker(n);
		cache.put(n, result);
		return result; 
	}
	
	private final static Group<Pair<Integer, Boolean>> mkWorker(int n) {
		final int n2 = n/2;
		final List<Boolean> booleans = new LinkedList<Boolean>() {{
			this.add(false);
			this.add(true);
		}};
		ImmutableIterable<Pair<Integer, Boolean>> elements = 
			cartesianProduct(IterableUtil.integerRange(0,n2), booleans);
		
		return new AbstractGroup<Pair<Integer,Boolean>>("D"+n, elements, n) {

			@Override
			public boolean contains(Pair<Integer, Boolean> x) {
				return (0 <= x.a && x.a < n2);
			}

			@Override
			public Pair<Integer, Boolean> inv(Pair<Integer, Boolean> x) {
				if (x.b) return x;
				return Pair.pair((n2-x.a)%n2, false); 
			}

			@Override
			public Pair<Integer, Boolean> mul(Pair<Integer, Boolean> x,
					Pair<Integer, Boolean> y) {
				if (!x.b && !y.b) {
					// r^xa s r^xb s
					return Pair.pair((x.a +y.a) % n2, false);
				}
				if (x.b && y.b) {
					// r^xa s r^xb s
					return Pair.pair((n2 + (x.a -y.a)) % n2, false);
				} 
				if (x.b && !y.b) {
					// r^xa s r^xb = r^xa s r^xb ss = r^xa r^-xb s
					return Pair.pair((n2 + (x.a - y.a)) % n2, true);
				}
				// r^xa r^xb s
				return Pair.pair((x.a + y.a) % n2, true);
				
			}

			@Override
			public Pair<Integer, Boolean> unit() {
				return Pair.pair(0, false);
			}

			@Override
			public String toShortString(Pair<Integer, Boolean> x) {
				return "r"+x.a + (x.b?"s": " ");
			}
			
			
		};
	}
}
